#include "listio.h"

#include "list.h"
#include "item.h"
#include "source.h"

#include <QDomDocument>
#include <QDomElement>
#include <QDomNodeList>
#include <QTextStream>

#include <QFile>
#include <QFileInfo>

#include <assert.h>

namespace
{
    static const QString T_DOCUMENT("EinkaufsListe");
    static const QString T_LIST("List");

    static const QString T_ITEM("Item");
    static const QString A_ITEM_NAME("Name");
    static const QString A_ITEM_QUANTITY("Quantity");
    static const QString A_ITEM_IGNORED("Ignored");

    static const QString T_SOURCE("Source");
    static const QString A_SOURCE_NAME("Name");
    static const QString A_SOURCE_QUANTITY("Quantity");
    static const QString A_SOURCE_IGNORED("Ignored");
    static const QString T_SOURCE_URL("Url");
    static const QString T_SOURCE_PRICE("Price");
}


ListIO::ListIO()
{
}

const bool ListIO::store(
    const QFileInfo &fileInfo
,   const List &list)
{
    if(fileInfo.exists() && !fileInfo.isWritable())
        return false;

    QFile file(fileInfo.absoluteFilePath());
    if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
       return false;

    QTextStream out(&file);


    QDomDocument document(T_DOCUMENT);

    QDomElement le = document.createElement(T_LIST);
    document.appendChild(le);

    foreach(const Item *item, list.items())
    {
        const QDomElement ie(toItemElement(document, *item));
        if(!ie.isNull())
            le.appendChild(ie);
        else
            return false;
    }

    document.save(out, 4);
    return true;
}


List *ListIO::restore(
    const QFileInfo &fileInfo)
{
    if(fileInfo.exists() && !fileInfo.isReadable())
        return NULL;

    QFile file(fileInfo.absoluteFilePath());
    if(!file.open(QIODevice::ReadOnly | QIODevice::Truncate))
       return NULL;

    List *list = new List();
    list->setName(fileInfo.fileName());

    QDomDocument document(T_LIST);
    document.setContent(&file);

    QDomElement le = document.firstChildElement(T_LIST);
    if(le.isNull())
        return NULL;

    for(int i = 0; i < le.childNodes().size(); ++i)
    {
        QDomNode node(le.childNodes().item(i));

        Item *item(fromItemElement(list, node.toElement()));

        if(item)
            list->addItem(item);
        else
            return NULL;
    }
    return list;
}


const QDomElement ListIO::toItemElement(
    QDomDocument &document
,   const Item &item)
{
    QDomElement ie = document.createElement(T_ITEM);

    ie.setAttribute(A_ITEM_NAME, item.name());
    ie.setAttribute(A_ITEM_QUANTITY, item.quantity());
    ie.setAttribute(A_ITEM_IGNORED, item.ignored());


    foreach(const Source *source, item.sources())
    {
        const QDomElement se(toSourceElement(document, *source));

        if(!se.isNull())
            ie.appendChild(se);
        else
            return QDomElement();
    }
    return ie;
}


const QDomElement ListIO::toSourceElement(
    QDomDocument &document
,   const Source &source)
{
    QDomElement se = document.createElement(T_SOURCE);

    QDomElement price = document.createElement(T_SOURCE_PRICE);
    price.appendChild(document.createTextNode(QString::number(source.price())));

    QDomElement url = document.createElement(T_SOURCE_URL);
    url.appendChild(document.createTextNode(source.url().toString()));

    se.setAttribute(A_SOURCE_NAME, source.name());
    se.setAttribute(A_SOURCE_QUANTITY, source.quantity());
    se.setAttribute(A_SOURCE_IGNORED, source.ignored());

    se.appendChild(price);
    se.appendChild(url);

    return se;
}

Item *ListIO::fromItemElement(
    List *list
,   const QDomElement &element)
{
    assert(list);

    if(element.isNull())
        return NULL;

    const QString name(element.attribute(A_ITEM_NAME, QObject::tr("Named Item")));
    Item *item(new Item(list->nameSpace(), name));

    const uint quantity(element.attribute(A_ITEM_QUANTITY
        , QString::number(INVALID_PRICE)).toUInt());
    item->setQuantity(quantity);

    const bool ignored(element.attribute(A_ITEM_IGNORED, false).toInt() ? true : false);
    item->setIgnored(ignored);

    for(int i = 0; i < element.childNodes().size(); ++i)
    {
        QDomNode node(element.childNodes().item(i));

        Source *source(fromSourceElement(item, node.toElement()));
        if(source)
            item->addSource(source);
        else
            return NULL;
    }
    return item;
}

Source *ListIO::fromSourceElement(
    Item *item
,   const QDomElement &element)
{
    assert(item);

    if(element.isNull())
        return NULL;

    const QString name(element.attribute(A_SOURCE_NAME, QObject::tr("Named Source")));

    QDomText price(element.firstChildElement(T_SOURCE_PRICE).firstChild().toText());
    QDomText url(element.firstChildElement(T_SOURCE_URL).firstChild().toText());

    Source *source(new Source(item->nameSpace()
        , name, price.data().toInt(), QUrl(url.data())));

    const uint quantity(element.attribute(A_SOURCE_QUANTITY
        , QString::number(INVALID_PRICE)).toUInt());
    source->setQuantity(quantity);

    const bool ignored(element.attribute(A_SOURCE_IGNORED, false).toInt() ? true : false);
    source->setIgnored(ignored);

    return source;
}
